package com.github.ecbp.user.service.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.ecbp.common.constant.ConstantCode;
import com.github.ecbp.common.data.entity.BaseEntity;
import com.github.ecbp.common.data.service.impl.BaseServiceImpl;
import com.github.ecbp.common.data.utils.TransformUtils;
import com.github.ecbp.common.req.BasePageDTO;
import com.github.ecbp.common.req.SimpleIdListStatusParam;
import com.github.ecbp.common.utils.Asserts;
import com.github.ecbp.user.service.common.UserCodeAndMessageEnum;
import com.github.ecbp.user.service.dto.GrantResourceDto;
import com.github.ecbp.user.service.dto.RoleDto;
import com.github.ecbp.user.service.entity.*;
import com.github.ecbp.user.service.mapper.MenuMapper;
import com.github.ecbp.user.service.mapper.RoleMapper;
import com.github.ecbp.user.service.mapper.RoleMenuRelationMapper;
import com.github.ecbp.user.service.mapper.RolePermissionRelationMapper;
import com.github.ecbp.user.service.request.RoleRelationRemoveReq;
import com.github.ecbp.user.service.response.RoleRes;
import com.github.ecbp.user.service.service.AdminRoleRelationService;
import com.github.ecbp.user.service.service.AdminService;
import com.github.ecbp.user.service.service.PermissionMenuRelationService;
import com.github.ecbp.user.service.service.RoleService;
import com.github.ecbp.user.service.vo.AdminRoleIdVo;
import com.github.ecbp.user.service.vo.ListInfoVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 后台用户角色表 服务实现类
 * </p>
 *
 * @author zj2626
 * @since 2020-03-18
 */
@Service
public class RoleServiceImpl extends BaseServiceImpl<RoleMapper, TRole> implements RoleService {
    @Autowired
    private AdminService adminService;
    @Autowired
    private AdminRoleRelationService adminRoleRelationService;
    @Autowired
    private PermissionMenuRelationService permissionMenuRelationService;
    @Autowired
    private MenuMapper menuMapper;
    @Autowired
    private RoleMenuRelationMapper roleMenuRelationMapper;
    @Autowired
    private RolePermissionRelationMapper rolePermissionRelationMapper;

    /**
     * 通过角色ID查询正在使用的有效用户数量
     *
     * @param ids
     * @return
     */
    @Override
    public List<ListInfoVo> pageInfoList(List<Long> ids) {
        return adminRoleRelationService.pageInfoList(ids);
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public int create(RoleDto param) {
        // 设置为当前类型用户默认角色
        if (param.getIfDefault()) {
            TRole oldRole = getDefaultRole(param.getCustomerType());
            Asserts.isTrue(null == oldRole, UserCodeAndMessageEnum.USER_ROLE_DEFAULT_SAVE_ERROR);
        }

        TRole entity = TransformUtils.copy(param, TRole.class);
        entity.setSort(0);
        int result = baseMapper.insert(entity);
        Asserts.checkHandle(result, UserCodeAndMessageEnum.USER_ROLE_SAVE_ERROR);

        // 分配权限
        GrantResourceDto grantResourceParam = new GrantResourceDto();
        grantResourceParam.setRoleId(entity.getId());
        grantResourceParam.setPermissionIds(param.getPermissionIds());
        grantResourceParam.setMenuIds(param.getMenuIds());
        allocToRole(grantResourceParam);
        return result;
    }

    /**
     * 修改指定角色信息和权限
     *
     * @param param
     * @return int
     * @author zj2626
     * @date 2020/5/24
     */
    @Override
    @Transactional(rollbackFor = Throwable.class)
    public int updateSelectiveById(RoleDto param) {
        TRole entity = TransformUtils.copy(param, TRole.class);

        // 设置为当前类型用户默认角色
        if (param.getIfDefault()) {
            TRole oldRole = getDefaultRole(param.getCustomerType());
            Asserts.isTrue(null == oldRole || param.getId().equals(oldRole.getId()),
                    UserCodeAndMessageEnum.USER_ROLE_DEFAULT_SAVE_ERROR);
        }

        entity.setId(param.getId());
        int result = baseMapper.updateById(entity);
        Asserts.checkHandle(result, UserCodeAndMessageEnum.USER_ROLE_SAVE_ERROR);

        // 分配权限
        GrantResourceDto grantResourceParam = new GrantResourceDto();
        grantResourceParam.setRoleId(entity.getId());
        grantResourceParam.setPermissionIds(param.getPermissionIds());
        grantResourceParam.setMenuIds(param.getMenuIds());
        allocToRole(grantResourceParam);
        return result;
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public int updateStatusById(SimpleIdListStatusParam param) {
        List<TRole> roleList = this.listByIds(param.getIds());
        Asserts.checkListNotNull(roleList, UserCodeAndMessageEnum.USER_ROLE_NOT_EXISTS_ERROR);

        LambdaQueryWrapper<TAdminRoleRelation> wrapper = new LambdaQueryWrapper<>(TAdminRoleRelation.class);
        wrapper.select(TAdminRoleRelation::getId);
        wrapper.in(TAdminRoleRelation::getRoleId, param.getIds());
        List<TAdminRoleRelation> count = adminRoleRelationService.list(wrapper);
        Asserts.isTrue(count.size() == 0, UserCodeAndMessageEnum.ROLE_USING_ERROR);

        for (Long id : param.getIds()) {
            TRole role = new TRole();
            role.setId(id);
            role.setStatus(param.getStatus());
            int result = baseMapper.updateById(role);
            Asserts.checkHandle(result, UserCodeAndMessageEnum.USER_ROLE_SAVE_ERROR);
        }
        return 1;
    }

    /**
     * 获得当前类型的默认角色
     *
     * @param customerType
     * @return
     */
    private TRole getDefaultRole(String customerType) {
        LambdaQueryWrapper<TRole> wrapper = new LambdaQueryWrapper<>(getEntityClass());
        wrapper.select(BaseEntity::getId);
        wrapper.eq(TRole::getIfDefault, true);
        List<TRole> roleList = baseMapper.selectList(wrapper);
        return CollectionUtils.isEmpty(roleList) ? null : roleList.get(0);
    }


    /**
     * 给角色分配菜单和资源,  这里传入资源信息,然后根据资源和菜单的关联同时分配菜单
     *
     * @param param
     * @return
     */
    private int allocToRole(GrantResourceDto param) {
        if (CollectionUtils.isEmpty(param.getPermissionIds()) && CollectionUtils.isEmpty(param.getMenuIds())) {
            return 0;
        }

        Set<TMenu> menus = new HashSet<>();
        // 得到对应的菜单
        if (!CollectionUtils.isEmpty(param.getMenuIds())) {
            LambdaQueryWrapper<TMenu> wrapper = new LambdaQueryWrapper<>(TMenu.class);
            wrapper.select(TMenu::getId, TMenu::getName);
            wrapper.in(BaseEntity::getId, param.getMenuIds());
            wrapper.eq(TMenu::getStatus, ConstantCode.TRUE);
            menus.addAll(new HashSet<>(menuMapper.selectList(wrapper)));
        }

        // 得到所有的资源
        if (!CollectionUtils.isEmpty(param.getPermissionIds())) {
            menus.addAll(permissionMenuRelationService.getMenuByPermissionId(new HashSet<>(param.getPermissionIds())));
        }
        if (CollectionUtils.isEmpty(menus)) {
            return 0;
        }

        // 得到菜单的父菜单...
        Set<Long> menuIds = new HashSet<>();
        Set<Long> currentMenuIds = menus.stream().map(TMenu::getId).collect(Collectors.toSet());
        getParentMenus(currentMenuIds, menuIds);

        RoleRelationRemoveReq roleRelationRemoveBo = new RoleRelationRemoveReq();
        roleRelationRemoveBo.setRoleIds(Collections.singleton(param.getRoleId()));
        // 赋予权限 ==> 角色 - 菜单
        final Set<Long> oldMenuIds = removeRoleMenuRelation(param.getRoleId());
        if (!CollectionUtils.isEmpty(menuIds)) {
            allocMenu(param.getRoleId(), menuIds);
            oldMenuIds.removeAll(menuIds);
        }
        roleRelationRemoveBo.setMenuIds(oldMenuIds);

        // 赋予权限 ==> 角色 - 资源
        final Set<Long> oldPermissionIds = removeRolePermissionRelation(param.getRoleId());
        if (!CollectionUtils.isEmpty(param.getPermissionIds())) {
            allocPermission(param.getRoleId(), new HashSet<>(param.getPermissionIds()));
            oldPermissionIds.removeAll(param.getPermissionIds());
        }
        roleRelationRemoveBo.setPermissionIds(oldPermissionIds);

        return 1;
    }

    /**
     * 获得当前菜单的所有父菜单 (数据库中菜单数据需要避免循环关系)
     *
     * @param currentMenuIds 当前菜单
     * @param menuIds        所有菜单(当前菜单+父菜单)
     */
    public void getParentMenus(Set<Long> currentMenuIds, Set<Long> menuIds) {
        LambdaQueryWrapper<TMenu> wrapper = new LambdaQueryWrapper<>(TMenu.class);
        wrapper.select(TMenu::getParentId);
        wrapper.in(BaseEntity::getId, currentMenuIds);
        wrapper.eq(TMenu::getStatus, ConstantCode.TRUE);
        List<TMenu> parentsMenus = menuMapper.selectList(wrapper);
        if (!CollectionUtils.isEmpty(parentsMenus)) {
            Set<Long> parentsMenuIds = parentsMenus.stream()
                    .filter(menu -> menu.getParentId() != 0)
                    .map(TMenu::getParentId).collect(Collectors.toSet());
            if (!CollectionUtils.isEmpty(parentsMenuIds)) {
                getParentMenus(parentsMenuIds, menuIds);
            }
        }
        menuIds.addAll(currentMenuIds);
    }

    @Override
    public RoleRes getById(Long id) {
        return TransformUtils.copy(baseMapper.selectById(id), RoleRes.class);
    }

    /**
     * 给角色分配菜单
     *
     * @param roleId
     * @param menuIds
     * @return
     */
    private void allocMenu(Long roleId, Set<Long> menuIds) {
        //批量插入新关系
        for (Long menuId : menuIds) {
            TRoleMenuRelation relation = new TRoleMenuRelation();
            relation.setRoleId(roleId);
            relation.setMenuId(menuId);
            Asserts.checkHandle(roleMenuRelationMapper.insert(relation), UserCodeAndMessageEnum.ROLE_MENU_SAVE_ERROR);
        }
    }

    private Set<Long> removeRoleMenuRelation(Long roleId) {
        //先删除原有关系
        LambdaQueryWrapper<TRoleMenuRelation> relationLambdaQueryWrapper = new LambdaQueryWrapper<>();
        relationLambdaQueryWrapper.select(TRoleMenuRelation::getId, TRoleMenuRelation::getMenuId);
        relationLambdaQueryWrapper.eq(TRoleMenuRelation::getRoleId, roleId);
        final List<TRoleMenuRelation> tRoleMenuRelations = roleMenuRelationMapper.selectList(relationLambdaQueryWrapper);
        if (CollectionUtils.isEmpty(tRoleMenuRelations)) {
            return Collections.emptySet();
        }
        Set<Long> ids = tRoleMenuRelations.stream().map(TRoleMenuRelation::getId).collect(Collectors.toSet());
        roleMenuRelationMapper.deleteBatchByIdsWithFill(new TRoleMenuRelation(), ids);

        return tRoleMenuRelations.stream().map(TRoleMenuRelation::getMenuId).collect(Collectors.toSet());
    }

    /**
     * 给角色分配资源
     *
     * @param roleId
     * @param permissionIds
     * @return
     */
    private void allocPermission(Long roleId, Set<Long> permissionIds) {
        //批量插入新关系
        for (Long permissionId : permissionIds) {
            TRolePermissionRelation relation = new TRolePermissionRelation();
            relation.setRoleId(roleId);
            relation.setPermissionId(permissionId);
            Asserts.checkHandle(rolePermissionRelationMapper.insert(relation), UserCodeAndMessageEnum.ROLE_PERMISSION_SAVE_ERROR);
        }
    }

    private Set<Long> removeRolePermissionRelation(Long roleId) {
        //先删除原有关系
        LambdaQueryWrapper<TRolePermissionRelation> relationLambdaQueryWrapper = new LambdaQueryWrapper<>();
        relationLambdaQueryWrapper.select(TRolePermissionRelation::getId, TRolePermissionRelation::getPermissionId);
        relationLambdaQueryWrapper.eq(TRolePermissionRelation::getRoleId, roleId);
        final List<TRolePermissionRelation> tRolePermissionRelations =
                rolePermissionRelationMapper.selectList(relationLambdaQueryWrapper);
        if (CollectionUtils.isEmpty(tRolePermissionRelations)) {
            return Collections.emptySet();
        }
        Set<Long> ids = tRolePermissionRelations.stream().map(TRolePermissionRelation::getId).collect(Collectors.toSet());
        rolePermissionRelationMapper.deleteBatchByIdsWithFill(new TRolePermissionRelation(), ids);

        return tRolePermissionRelations.stream().map(TRolePermissionRelation::getPermissionId).collect(Collectors.toSet());
    }

    @Override
    public List<RoleRes> getRoleListByAdminId(Long adminId) {
        List<AdminRoleIdVo> adminRoleRelations = adminRoleRelationService.getRoleIdByAdminId(adminId);

        if (!CollectionUtils.isEmpty(adminRoleRelations)) {
            LambdaQueryWrapper<TRole> wrapper = new LambdaQueryWrapper<>(TRole.class);
            wrapper.in(BaseEntity::getId, adminRoleRelations.stream().map(AdminRoleIdVo::getRoleId).collect(Collectors.toSet()));
            return TransformUtils.listCopy(baseMapper.selectList(wrapper), RoleRes.class);
        }

        return null;
    }

    /**
     * 重新加载权限相关的用户的权限信息
     *
     * @param ids
     */
    @Override
    public void reloadUserPermission(Set<Long> ids) {
        final List<TAdmin> adminIdByRoleId = adminRoleRelationService.getAdminIdByRoleId(new HashSet<>(ids));
        if (!CollectionUtils.isEmpty(adminIdByRoleId)) {
            Set<String> loginPhones = adminIdByRoleId.stream().map(TAdmin::getLoginPhone).collect(Collectors.toSet());
            adminService.reloadUserPermission(loginPhones);
        }
    }

    @Override
    protected void assemble(TRole entity, LambdaQueryWrapper<TRole> wrapper, BasePageDTO pageDTO) {
        wrapper.select(BaseEntity::getId, TRole::getName, TRole::getStatus, BaseEntity::getUpdateTime);
        if (null != entity.getStatus()) {
            wrapper.eq(TRole::getStatus, entity.getStatus());
        }
        if (!StringUtils.isEmpty(entity.getName())) {
            wrapper.likeRight(TRole::getName, entity.getName());
        }
        wrapper.orderByDesc(BaseEntity::getCreateTime);
    }
}
